'''

ga-algorithm2.0
写在前面
一个中心，n个节点
@@@@这个算法基于以下条件/局限性@@@@
# 将遗传算法封装到类中
# 数据从Excel表格导入，长短可变
# 选择的优先条件是距离的长短，而非车的数量7
# 车辆数不固定，根据题目的需求来变化
# 有两种载重恒定的车车*****************
# 解决2种车的问题： 如果有某一条路径的载重量 < 小车的载重量，则这条路由小车负责哦*****************
# 所有的节点之间都可以直连，而非给定路径的城市
# 加入每个工作人员的上班时间*********************************
# 给出更详细的数据，包括：*********************************
    i、最终每条路径的长度，所花时间
    ii、最终每条路径的所配车辆

'''
import xlrd
import numpy as np
from copy import deepcopy
import matplotlib.pyplot as plt
from deap import base, tools, creator, algorithms
import random
from queue import Queue
from threading import Thread, RLock
from GAFunctions import *


class GA(object):
    def __init__(self, dataPara):
        self.PopulationDeliver = Queue()
        self.BestDeliver = Queue()
        self.AVGDeliver = Queue()
        self.BestIndividual = Queue()
        self.FinalOutputStr = ''
        self.FinalOutput = Queue()
        self.EvolutionQueue = Queue()
        self.bestInd = None
        self.distributionPlan = None
        self.workTime = None
        self.distances = None
        self.loads = None
        self.bestFit = None
        self.logs = None
        self.pop = None
        # 用字典存储所有参数 -- 配送中心坐标、顾客坐标、顾客需求、到达时间窗口、服务时间、车型载重量
        self.dataDict = {}

        # 节点坐标，节点0是配送中心的坐标
        self.dataDict['NodeCoor'] = dataPara['NodesLocation']
        # 需求表，将配送中心的需求设置为0
        self.dataDict['Demand'] = dataPara['NodesDemand']
        self.dataDict['CarIsChangeAble'] = dataPara['CarIsChangeAble']
        self.dataDict['CarIsNotChangeAble'] = dataPara['CarIsNotChangeAble']
        self.dataDict['OneTypeCar'] = dataPara['OneTypeCar']
        self.dataDict['TwoTypeCar'] = dataPara['TwoTypeCar']
        # 设置车辆最大载荷
        if dataPara['Load_1'] != '' and dataPara['Load_2'] != '':
            if float(dataPara['Load_1']) > float(dataPara['Load_2']):
                self.dataDict['MaxLoad'] = float(dataPara['Load_1'])
                self.dataDict['LittleCar'] = float(dataPara['Load_2'])
            else:
                self.dataDict['MaxLoad'] = float(dataPara['Load_2'])
                self.dataDict['LittleCar'] = float(dataPara['Load_1'])
        elif dataPara['Load_2'] == '' and dataPara['Load_1'] != '':
            self.dataDict['MaxLoad'] = float(dataPara['Load_1'])
            self.dataDict['LittleCar'] = float(dataPara['Load_1'])
        else:
            self.dataDict['MaxLoad'] = 0
            self.dataDict['LittleCar'] = 0
            pass
        self.dataDict['CarNumSet'] = float(dataPara['CarNumSet'])
        # 设置卸货时间
        self.dataDict['UnloadTime'] = float(dataPara['UnloadTime'])
        # 设置福报
        self.dataDict['WorkTime'] = dataPara['TimeLimit'] * 60
        # 设置车辆最大里程
        self.dataDict['LengthLimit'] = dataPara['LengthLimit']
        # 设置车速
        self.dataDict['Speed'] = dataPara['CarSpeed']
        self.dataDict['GenerationsNumber'] = dataPara['GenerationsNumber']
        self.dataDict['ngen'] = 400
        self.GA_Tools = GA_Functions(self.dataDict)

    def GAProcess(self):

        # 遗传算法开始=================================
        # 问题定义
        creator.create('FitnessMin', base.Fitness, weights=(-1.0,))  # 最小化问题
        # 给个体一个routes属性用来记录其表示的路线
        creator.create('Individual', list, fitness=creator.FitnessMin)
        self.toolbox = base.Toolbox()
        if self.dataDict['CarIsChangeAble']:
            # 注册遗传算法操作
            self.toolbox.register('genInd', self.GA_Tools.genInd1, dataDict=self.dataDict)
            self.toolbox.register('individual', tools.initIterate, creator.Individual, self.toolbox.genInd)
            self.toolbox.register('population', tools.initRepeat, list, self.toolbox.individual)
            self.toolbox.register('evaluate', self.GA_Tools.evaluate)
            self.toolbox.register('select', tools.selTournament, tournsize=2)
            self.toolbox.register('mate', self.GA_Tools.crossover)
            self.toolbox.register('mutate', self.GA_Tools.mutate)
        elif self.dataDict['CarIsNotChangeAble']:
            # 注册遗传算法操作
            self.toolbox.register('genInd', self.GA_Tools.genInd2, dataDict=self.dataDict)
            self.toolbox.register('individual', tools.initIterate, creator.Individual, self.toolbox.genInd)
            self.toolbox.register('population', tools.initRepeat, list, self.toolbox.individual)
            self.toolbox.register('evaluate', self.GA_Tools.evaluate)
            self.toolbox.register('select', tools.selTournament, tournsize=2)
            self.toolbox.register('mate', self.GA_Tools.crossover)
            self.toolbox.register('mutate', self.GA_Tools.mutate)
        else:
            # 注册遗传算法操作
            self.toolbox.register('genInd', self.GA_Tools.genInd1, dataDict=self.dataDict)
            self.toolbox.register('individual', tools.initIterate, creator.Individual, self.toolbox.genInd)
            self.toolbox.register('population', tools.initRepeat, list, self.toolbox.individual)
            self.toolbox.register('evaluate', self.GA_Tools.evaluate)
            self.toolbox.register('select', tools.selTournament, tournsize=2)
            self.toolbox.register('mate', self.GA_Tools.crossover)
            self.toolbox.register('mutate', self.GA_Tools.mutate)

        # 生成初始族群
        self.toolbox.popSize = 100
        self.pop = self.toolbox.population(self.toolbox.popSize)

        # 记录迭代数据
        self.stats = tools.Statistics(key=lambda ind: ind.fitness.values)
        self.stats.register('min', np.min)
        self.stats.register('avg', np.mean)
        self.stats.register('std', np.std)
        self.hallOfFame = tools.HallOfFame(maxsize=self.toolbox.popSize / 3)

        # 遗传算法参数
        self.toolbox.ngen = self.dataDict['ngen']
        self.toolbox.cxpb = 0.8
        self.toolbox.mutpb = 0.1

        thread = Thread(target=self.MainFunc)
        thread.start()

    def MainFunc(self):
        threadLock.acquire()
        # 遗传算法主程序
        self.pop, self.logs, self.hallOfFame = GA_Functions.eaMuPlusLambda(self.pop, self.toolbox,
                                                                           mu=self.toolbox.popSize,
                                                                           lambda_=self.toolbox.popSize,
                                                                           cxpb=self.toolbox.cxpb,
                                                                           mutpb=self.toolbox.mutpb,
                                                                           ngen=self.toolbox.ngen,
                                                                           LogDeliver=self.EvolutionQueue,
                                                                           BestIndividual=self.BestIndividual,
                                                                           BestDeliver=self.BestDeliver,
                                                                           AvgDeliver=self.AVGDeliver,
                                                                           PopulationDeliver=self.PopulationDeliver,
                                                                           stats=self.stats,
                                                                           halloffame=self.hallOfFame,
                                                                           verbose=True)

        self.bestInd = self.hallOfFame.items[0]
        self.distributionPlan = self.GA_Tools.decodeInd(self.bestInd)
        self.bestFit = self.bestInd.fitness.values
        self.loads = self.GA_Tools.calLoad(self.distributionPlan)
        self.distances = self.GA_Tools.calSubRouteLen(self.distributionPlan, self.dataDict)
        self.workTime = self.GA_Tools.calRouteWorkTime(self.distributionPlan, self.dataDict)
        threadLock.release()

        self.FinalOutputStr += '最佳运输计划为：\n\n'
        if self.dataDict['CarIsChangeAble']:
            if self.dataDict['TwoTypeCar']:
                for i in range(0, len(self.distributionPlan)):
                    loads = self.GA_Tools.calLoad(self.distributionPlan)
                    self.FinalOutputStr += '第' + str(i + 1) + '条路径\n'
                    self.FinalOutputStr += str(self.distributionPlan[i])
                    if loads[i] <= self.dataDict['LittleCar']:
                        self.FinalOutputStr += '\n此路总重量为:\n' + str(loads[i]) + ' \n使用载重为:\n' + str(
                            self.dataDict['LittleCar']) + '的小车\n'
                        self.FinalOutputStr += '此路总长为：\n' + str(self.distances[i]) + '公里\n'
                        self.FinalOutputStr += '此路工作时长为：\n' + str(self.workTime[i] / 60) + '小时 \n=========================\n'
                    else:
                        self.FinalOutputStr += '\n此路总重量为:\n' + str(loads[i]) + ' \n使用载重为:\n' + str(
                            self.dataDict['MaxLoad']) + '的大车\n'
                        self.FinalOutputStr += '此路总长为：\n' + str(self.distances[i]) + '公里\n'
                        self.FinalOutputStr += '此路工作时长为：\n' + str(self.workTime[i] / 60) + '小时 \n=========================\n'
                self.FinalOutputStr += '最短运输距离为：\n'
                self.FinalOutputStr += str(self.bestFit) + '\n'
            elif self.dataDict['OneTypeCar']:
                for i in range(0, len(self.distributionPlan)):
                    loads = self.GA_Tools.calLoad(self.distributionPlan)
                    self.FinalOutputStr += '第' + str(i + 1) + '条路径\n'
                    self.FinalOutputStr += str(self.distributionPlan[i])
                    self.FinalOutputStr += '\n此路总重量为:\n' + str(loads[i]) + ' \n使用载重为:\n' + str(
                        self.dataDict['MaxLoad']) + '的车\n'
                    self.FinalOutputStr += '此路总长为：\n' + str(self.distances[i]) + '公里\n'
                    self.FinalOutputStr += '此路工作时长为：\n' + str(
                        self.workTime[i] / 60) + '小时 \n=========================\n'
                self.FinalOutputStr += '最短运输距离为：\n'
                self.FinalOutputStr += str(self.bestFit) + '\n'
            else:
                for i in range(0, len(self.distributionPlan)):
                    loads = self.GA_Tools.calLoad(self.distributionPlan)
                    self.FinalOutputStr += '第' + str(i + 1) + '条路径\n'
                    self.FinalOutputStr += str(self.distributionPlan[i])
                    self.FinalOutputStr += '\n此路总重量为:\n' + str(loads[i]) + ' \n使用载重为:\n' + str(
                        self.dataDict['MaxLoad']) + '的车\n'
                    self.FinalOutputStr += '此路总长为：\n' + str(self.distances[i]) + '公里\n'
                    self.FinalOutputStr += '此路工作时长为：\n' + str(
                        self.workTime[i] / 60) + '小时 \n=========================\n'
                self.FinalOutputStr += '最短运输距离为：\n'
                self.FinalOutputStr += str(self.bestFit) + '\n'
        elif self.dataDict['CarIsNotChangeAble']:
            for i in range(0, len(self.distributionPlan)):
                loads = self.GA_Tools.calLoad(self.distributionPlan)
                self.FinalOutputStr += '第' + str(i + 1) + '条路径\n'
                self.FinalOutputStr += str(self.distributionPlan[i])
                self.FinalOutputStr += '\n此路总重量为:\n' + str(loads[i]) + ' \n使用载重为:\n' + str(
                        self.dataDict['MaxLoad']) + '的车\n'
                self.FinalOutputStr += '此路总长为：\n' + str(self.distances[i]) + '公里\n'
                self.FinalOutputStr += '此路工作时长为：\n' + str(self.workTime[i] / 60) + '小时 \n=========================\n'
            self.FinalOutputStr += '最短运输距离为：\n'
            self.FinalOutputStr += str(self.bestFit) + '\n'
        else:
            for i in range(0, len(self.distributionPlan)):
                loads = self.GA_Tools.calLoad(self.distributionPlan)
                self.FinalOutputStr += '第' + str(i + 1) + '条路径\n'
                self.FinalOutputStr += str(self.distributionPlan[i])
                self.FinalOutputStr += '\n此路总重量为:\n' + str(loads[i]) + ' \n使用载重为:\n' + str(
                    self.dataDict['MaxLoad']) + '的车\n'
                self.FinalOutputStr += '此路总长为：\n' + str(self.distances[i]) + '公里\n'
                self.FinalOutputStr += '此路工作时长为：\n' + str(self.workTime[i] / 60) + '小时 \n=========================\n'
            self.FinalOutputStr += '最短运输距离为：\n'
            self.FinalOutputStr += str(self.bestFit) + '\n'

        self.FinalOutput.put(self.FinalOutputStr)

        # 画出迭代图xcc
        minFit = self.logs.select('min')
        avgFit = self.logs.select('avg')
        plt.plot(minFit, 'b-', label='Minimum Fitness')
        plt.plot(avgFit, 'r-', label='Average Fitness')
        plt.xlabel('# Gen')
        plt.ylabel('Fitness')
        plt.legend(loc='best')
        plt.show()


if __name__ == "__main__":
    ga = GA()
    ga.GAProcess()
    ga.show()
